home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / sh-utils.12 / sh-utils / sh-utils-1.12 / src / printf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  10.7 KB  |  533 lines

  1. /* printf - format and print data
  2.    Copyright (C) 90, 91, 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: printf format [argument...]
  19.  
  20.    A front end to the printf function that lets it be used from the shell.
  21.  
  22.    Backslash escapes:
  23.  
  24.    \" = double quote
  25.    \\ = backslash
  26.    \a = alert (bell)
  27.    \b = backspace
  28.    \c = produce no further output
  29.    \f = form feed
  30.    \n = new line
  31.    \r = carriage return
  32.    \t = horizontal tab
  33.    \v = vertical tab
  34.    \0ooo = octal number (ooo is 0 to 3 digits)
  35.    \xhhh = hexadecimal number (hhh is 1 to 3 digits)
  36.  
  37.    Additional directive:
  38.  
  39.    %b = print an argument string, interpreting backslash escapes
  40.  
  41.    The `format' argument is re-used as many times as necessary
  42.    to convert all of the given arguments.
  43.  
  44.    David MacKenzie <djm@gnu.ai.mit.edu> */
  45.  
  46. #include <config.h>
  47. #include <stdio.h>
  48. #include <sys/types.h>
  49. #include <getopt.h>
  50.  
  51. #include "system.h"
  52. #include "version.h"
  53. #include "long-options.h"
  54.  
  55. #ifndef STDC_HEADERS
  56. double strtod ();
  57. long strtol ();
  58. unsigned long strtoul ();
  59. #endif
  60.  
  61. #define isodigit(c) ((c) >= '0' && (c) <= '7')
  62. #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
  63. #define octtobin(c) ((c) - '0')
  64.  
  65. char *xmalloc ();
  66. void error ();
  67.  
  68. static double xstrtod ();
  69. static int print_esc ();
  70. static int print_formatted ();
  71. static long xstrtol ();
  72. static unsigned long xstrtoul ();
  73. static void print_direc ();
  74. static void print_esc_char ();
  75. static void print_esc_string ();
  76. static void verify ();
  77.  
  78. /* The value to return to the calling program.  */
  79. static int exit_status;
  80.  
  81. /* The name this program was run with. */
  82. char *program_name;
  83.  
  84. static void
  85. usage (status)
  86.      int status;
  87. {
  88.   if (status != 0)
  89.     fprintf (stderr, "Try `%s --help' for more information.\n",
  90.          program_name);
  91.   else
  92.     {
  93.       printf ("\
  94. Usage: %s FORMAT [ARGUMENT]...\n\
  95.   or:  %s OPTION\n\
  96. ",
  97.           program_name, program_name);
  98.       printf ("\
  99. \n\
  100.   --help      display this help and exit\n\
  101.   --version   output version information and exit\n\
  102. \n\
  103. FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
  104. \n\
  105.   \\\"      double quote\n\
  106.   \\0NNN   character with octal value NNN (0 to 3 digits)\n\
  107.   \\\\      backslash\n\
  108.   \\a      alert (BEL)\n\
  109.   \\b      backspace\n\
  110.   \\c      produce no further output\n\
  111.   \\f      form feed\n\
  112.   \\n      new line\n\
  113.   \\r      carriage return\n\
  114.   \\t      horizontal tab\n\
  115.   \\v      vertical tab\n\
  116.   \\xNNN   character with hexadecimal value NNN (1 to 3 digits)\n\
  117. \n\
  118.   %%%%      a single %%\n\
  119.   %%b      ARGUMENT as a string with `\\' escapes interpreted\n\
  120. \n\
  121. and all C format specifications ending with one of diouxXfeEgGcs, with\n\
  122. ARGUMENTs converted to proper type first.  Variable widths are handled.\n\
  123. ");
  124.     }
  125.   exit (status);
  126. }
  127.  
  128. void
  129. main (argc, argv)
  130.      int argc;
  131.      char **argv;
  132. {
  133.   char *format;
  134.   int args_used;
  135.  
  136.   program_name = argv[0];
  137.   exit_status = 0;
  138.  
  139.   parse_long_options (argc, argv, "printf", version_string, usage);
  140.  
  141.   if (argc == 1)
  142.     {
  143.       fprintf (stderr, "Usage: %s format [argument...]\n", program_name);
  144.       exit (1);
  145.     }
  146.  
  147.   format = argv[1];
  148.   argc -= 2;
  149.   argv += 2;
  150.  
  151.   do
  152.     {
  153.       args_used = print_formatted (format, argc, argv);
  154.       argc -= args_used;
  155.       argv += args_used;
  156.     }
  157.   while (args_used > 0 && argc > 0);
  158.  
  159.   exit (exit_status);
  160. }
  161.  
  162. /* Print the text in FORMAT, using ARGV (with ARGC elements) for
  163.    arguments to any `%' directives.
  164.    Return the number of elements of ARGV used.  */
  165.  
  166. static int
  167. print_formatted (format, argc, argv)
  168.      char *format;
  169.      int argc;
  170.      char **argv;
  171. {
  172.   int save_argc = argc;        /* Preserve original value.  */
  173.   char *f;            /* Pointer into `format'.  */
  174.   char *direc_start;        /* Start of % directive.  */
  175.   size_t direc_length;        /* Length of % directive.  */
  176.   int field_width;        /* Arg to first '*', or -1 if none.  */
  177.   int precision;        /* Arg to second '*', or -1 if none.  */
  178.  
  179.   for (f = format; *f; ++f)
  180.     {
  181.       switch (*f)
  182.     {
  183.     case '%':
  184.       direc_start = f++;
  185.       direc_length = 1;
  186.       field_width = precision = -1;
  187.       if (*f == '%')
  188.         {
  189.           putchar ('%');
  190.           break;
  191.         }
  192.       if (*f == 'b')
  193.         {
  194.           if (argc > 0)
  195.         {
  196.           print_esc_string (*argv);
  197.           ++argv;
  198.           --argc;
  199.         }
  200.           break;
  201.         }
  202.       if (index ("-+ #", *f))
  203.         {
  204.           ++f;
  205.           ++direc_length;
  206.         }
  207.       if (*f == '*')
  208.         {
  209.           ++f;
  210.           ++direc_length;
  211.           if (argc > 0)
  212.         {
  213.           field_width = xstrtoul (*argv);
  214.           ++argv;
  215.           --argc;
  216.         }
  217.           else
  218.         field_width = 0;
  219.         }
  220.       else
  221.         while (ISDIGIT (*f))
  222.           {
  223.         ++f;
  224.         ++direc_length;
  225.           }
  226.       if (*f == '.')
  227.         {
  228.           ++f;
  229.           ++direc_length;
  230.           if (*f == '*')
  231.         {
  232.           ++f;
  233.           ++direc_length;
  234.           if (argc > 0)
  235.             {
  236.               precision = xstrtoul (*argv);
  237.               ++argv;
  238.               --argc;
  239.             }
  240.           else
  241.             precision = 0;
  242.         }
  243.           else
  244.         while (ISDIGIT (*f))
  245.           {
  246.             ++f;
  247.             ++direc_length;
  248.           }
  249.         }
  250.       if (*f == 'l' || *f == 'L' || *f == 'h')
  251.         {
  252.           ++f;
  253.           ++direc_length;
  254.         }
  255.       if (!index ("diouxXfeEgGcs", *f))
  256.         error (1, 0, "%%%c: invalid directive", *f);
  257.       ++direc_length;
  258.       if (argc > 0)
  259.         {
  260.           print_direc (direc_start, direc_length, field_width,
  261.                precision, *argv);
  262.           ++argv;
  263.           --argc;
  264.         }
  265.       else
  266.         print_direc (direc_start, direc_length, field_width,
  267.              precision, "");
  268.       break;
  269.  
  270.     case '\\':
  271.       f += print_esc (f);
  272.       break;
  273.  
  274.     default:
  275.       putchar (*f);
  276.     }
  277.     }
  278.  
  279.   return save_argc - argc;
  280. }
  281.  
  282. /* Print a \ escape sequence starting at ESCSTART.
  283.    Return the number of characters in the escape sequence
  284.    besides the backslash. */
  285.  
  286. static int
  287. print_esc (escstart)
  288.      char *escstart;
  289. {
  290.   register char *p = escstart + 1;
  291.   int esc_value = 0;        /* Value of \nnn escape. */
  292.   int esc_length;        /* Length of \nnn escape. */
  293.  
  294.   /* \0ooo and \xhhh escapes have maximum length of 3 chars. */
  295.   if (*p == 'x')
  296.     {
  297.       for (esc_length = 0, ++p;
  298.        esc_length < 3 && ISXDIGIT (*p);
  299.        ++esc_length, ++p)
  300.     esc_value = esc_value * 16 + hextobin (*p);
  301.       if (esc_length == 0)
  302.     error (1, 0, "missing hexadecimal number in escape");
  303.       putchar (esc_value);
  304.     }
  305.   else if (*p == '0')
  306.     {
  307.       for (esc_length = 0, ++p;
  308.        esc_length < 3 && isodigit (*p);
  309.        ++esc_length, ++p)
  310.     esc_value = esc_value * 8 + octtobin (*p);
  311.       putchar (esc_value);
  312.     }
  313.   else if (index ("\"\\abcfnrtv", *p))
  314.     print_esc_char (*p++);
  315.   else
  316.     error (1, 0, "\\%c: invalid escape", *p);
  317.   return p - escstart - 1;
  318. }
  319.  
  320. /* Output a single-character \ escape.  */
  321.  
  322. static void
  323. print_esc_char (c)
  324.      char c;
  325. {
  326.   switch (c)
  327.     {
  328.     case 'a':            /* Alert. */
  329.       putchar (7);
  330.       break;
  331.     case 'b':            /* Backspace. */
  332.       putchar (8);
  333.       break;
  334.     case 'c':            /* Cancel the rest of the output. */
  335.       exit (0);
  336.       break;
  337.     case 'f':            /* Form feed. */
  338.       putchar (12);
  339.       break;
  340.     case 'n':            /* New line. */
  341.       putchar (10);
  342.       break;
  343.     case 'r':            /* Carriage return. */
  344.       putchar (13);
  345.       break;
  346.     case 't':            /* Horizontal tab. */
  347.       putchar (9);
  348.       break;
  349.     case 'v':            /* Vertical tab. */
  350.       putchar (11);
  351.       break;
  352.     default:
  353.       putchar (c);
  354.       break;
  355.     }
  356. }
  357.  
  358. /* Print string STR, evaluating \ escapes. */
  359.  
  360. static void
  361. print_esc_string (str)
  362.      char *str;
  363. {
  364.   for (; *str; str++)
  365.     if (*str == '\\')
  366.       str += print_esc (str);
  367.     else
  368.       putchar (*str);
  369. }
  370.  
  371. /* Output a % directive.  START is the start of the directive,
  372.    LENGTH is its length, and ARGUMENT is its argument.
  373.    If FIELD_WIDTH or PRECISION is non-negative, they are args for
  374.    '*' values in those fields. */
  375.  
  376. static void
  377. print_direc (start, length, field_width, precision, argument)
  378.      char *start;
  379.      size_t length;
  380.      int field_width;
  381.      int precision;
  382.      char *argument;
  383. {
  384.   char *p;        /* Null-terminated copy of % directive. */
  385.  
  386.   p = xmalloc ((unsigned) (length + 1));
  387.   strncpy (p, start, length);
  388.   p[length] = 0;
  389.  
  390.   switch (p[length - 1])
  391.     {
  392.     case 'd':
  393.     case 'i':
  394.       if (field_width < 0)
  395.     {
  396.       if (precision < 0)
  397.         printf (p, xstrtol (argument));
  398.       else
  399.         printf (p, precision, xstrtol (argument));
  400.     }
  401.       else
  402.     {
  403.       if (precision < 0)
  404.         printf (p, field_width, xstrtol (argument));
  405.       else
  406.         printf (p, field_width, precision, xstrtol (argument));
  407.     }
  408.       break;
  409.  
  410.     case 'o':
  411.     case 'u':
  412.     case 'x':
  413.     case 'X':
  414.       if (field_width < 0)
  415.     {
  416.       if (precision < 0)
  417.         printf (p, xstrtoul (argument));
  418.       else
  419.         printf (p, precision, xstrtoul (argument));
  420.     }
  421.       else
  422.     {
  423.       if (precision < 0)
  424.         printf (p, field_width, xstrtoul (argument));
  425.       else
  426.         printf (p, field_width, precision, xstrtoul (argument));
  427.     }
  428.       break;
  429.  
  430.     case 'f':
  431.     case 'e':
  432.     case 'E':
  433.     case 'g':
  434.     case 'G':
  435.       if (field_width < 0)
  436.     {
  437.       if (precision < 0)
  438.         printf (p, xstrtod (argument));
  439.       else
  440.         printf (p, precision, xstrtod (argument));
  441.     }
  442.       else
  443.     {
  444.       if (precision < 0)
  445.         printf (p, field_width, xstrtod (argument));
  446.       else
  447.         printf (p, field_width, precision, xstrtod (argument));
  448.     }
  449.       break;
  450.  
  451.     case 'c':
  452.       printf (p, *argument);
  453.       break;
  454.  
  455.     case 's':
  456.       if (field_width < 0)
  457.     {
  458.       if (precision < 0)
  459.         printf (p, argument);
  460.       else
  461.         printf (p, precision, argument);
  462.     }
  463.       else
  464.     {
  465.       if (precision < 0)
  466.         printf (p, field_width, argument);
  467.       else
  468.         printf (p, field_width, precision, argument);
  469.     }
  470.       break;
  471.     }
  472.  
  473.   free (p);
  474. }
  475.  
  476. static unsigned long
  477. xstrtoul (s)
  478.      char *s;
  479. {
  480.   char *end;
  481.   unsigned long val;
  482.  
  483.   errno = 0;
  484.   val = strtoul (s, &end, 0);
  485.   verify (s, end);
  486.   return val;
  487. }
  488.  
  489. static long
  490. xstrtol (s)
  491.      char *s;
  492. {
  493.   char *end;
  494.   long val;
  495.  
  496.   errno = 0;
  497.   val = strtol (s, &end, 0);
  498.   verify (s, end);
  499.   return val;
  500. }
  501.  
  502. static double
  503. xstrtod (s)
  504.      char *s;
  505. {
  506.   char *end;
  507.   double val;
  508.  
  509.   errno = 0;
  510.   val = strtod (s, &end);
  511.   verify (s, end);
  512.   return val;
  513. }
  514.  
  515. static void
  516. verify (s, end)
  517.      char *s, *end;
  518. {
  519.   if (errno)
  520.     {
  521.       error (0, errno, "%s", s);
  522.       exit_status = 1;
  523.     }
  524.   else if (*end)
  525.     {
  526.       if (s == end)
  527.     error (0, 0, "%s: expected a numeric value", s);
  528.       else
  529.     error (0, 0, "%s: value not completely converted", s);
  530.       exit_status = 1;
  531.     }
  532. }
  533.